package com.yy.hi.library.excutor

import android.os.Handler
import android.os.Looper
import androidx.annotation.IntRange
import com.yy.hi.library.hilog.HiLog
import java.util.concurrent.BlockingQueue
import java.util.concurrent.PriorityBlockingQueue
import java.util.concurrent.ThreadFactory
import java.util.concurrent.ThreadPoolExecutor
import java.util.concurrent.TimeUnit
import java.util.concurrent.atomic.AtomicLong
import java.util.concurrent.locks.Condition
import java.util.concurrent.locks.ReentrantLock

/**
 * User: gongyongfeng2@yy.com
 * Date: 2020/7/1 19 :53
 */

object HiExecutor {
    private val hiExecutor: ThreadPoolExecutor
    private val lock: ReentrantLock = ReentrantLock()
    private val condition: Condition
    private var isPause = false
    private val mainHandler = Handler(Looper.getMainLooper())

    init {
        condition = lock.newCondition()
        val cpuCount = Runtime.getRuntime().availableProcessors()
        val corePoolSize = cpuCount + 1
        val maxPoolSize = corePoolSize * 2 + 1
        val blockingQueue: PriorityBlockingQueue<out Runnable> = PriorityBlockingQueue()
        val keepAliveTime = 20L
        val unit = TimeUnit.SECONDS
        val seq = AtomicLong()
        val threadFactory = ThreadFactory {
            val thread = Thread(it)
            thread.name = "hi-executor" + seq.getAndIncrement()
            return@ThreadFactory thread
        }
        hiExecutor =
            object : ThreadPoolExecutor(corePoolSize, maxPoolSize, keepAliveTime, unit,
                blockingQueue as BlockingQueue<Runnable>,
                threadFactory) {
                override fun beforeExecute(t: Thread?, r: Runnable?) {
                    super.beforeExecute(t, r)
                    if (isPause) {
                        lock.lock()
                        try {
                            condition.await()
                        } finally {
                            lock.unlock()
                        }
                    }
                }

                override fun afterExecute(r: Runnable?, t: Throwable?) {
                    super.afterExecute(r, t)
                    HiLog.e("executor priority is " + (r as PriorityRunnable).priority)
                }
            }
    }

    @JvmOverloads
    fun execute(@IntRange(from = 0, to = 10) priority: Int = 0, runnable: Runnable) {
        hiExecutor.execute(PriorityRunnable(priority, runnable))
    }

    @JvmOverloads
    fun execute(@IntRange(from = 0, to = 10) priority: Int = 0, runnable: Callable<*>) {
        hiExecutor.execute(PriorityRunnable(priority, runnable))
    }

    @Synchronized
    fun pause() {
        isPause = true
        HiLog.e("executor is pause")
    }

    @Synchronized
    fun resume() {
        isPause = false
        lock.lock()
        try {
            condition.signalAll()
        } finally {
            lock.unlock()
        }
        HiLog.e("executor is pause")
    }

    abstract class Callable<T> : Runnable {
        override fun run() {
            mainHandler.post { onPrePare() }
            val t: T = onBackground()
            mainHandler.post { onCompleted() }
        }

        open fun onPrePare() {
        }

        abstract fun onBackground(): T

        abstract fun onCompleted(): T
    }

    class PriorityRunnable(val priority: Int, private val runnable: Runnable) : Runnable,
        Comparable<PriorityRunnable> {
        override fun run() {
            runnable.run()
        }

        override fun compareTo(other: PriorityRunnable): Int {
            return if (this.priority < other.priority) {
                1
            } else if (this.priority > other.priority) {
                -1
            } else {
                0
            }
        }
    }
}